﻿//---------------------------------------------------------------------------
// <copyright file="Ribbon.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
    #region Using declarations

    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Windows.Markup;

    #endregion Using declarations

    /// <summary>
    ///   The main Ribbon control which consists of multiple tabs, each of which
    ///   containing groups of controls.  The Ribbon also provides improved context
    ///   menus, enhanced screen tips, and keyboard shortcuts.
    /// </summary>
    [ContentProperty("Tabs")]
    public class Ribbon : Control
    {
        #region Fields
        
        /// <summary>
        ///   The minimum allowed width before the Ribbon will be collapsed.
        /// </summary>
        private const double CollapseWidth = 300.0;

        /// <summary>
        ///   The minimum allowed height before the Ribbon will be collapsed.
        /// </summary>
        private const double CollapseHeight = 250.0;

        /// <summary>
        ///   Backing store for the ContextualTabs property.
        /// </summary>
        private ObservableCollection<RibbonTab> _contextualTabs;

        /// <summary>
        ///   Backing store for the ContextualTabGroups property.
        /// </summary>
        private ObservableCollection<RibbonContextualTabGroup> _tabGroups;

        /// <summary>
        ///   Backing store for the Tabs property.
        /// </summary>
        private ObservableCollection<RibbonTab> _tabs;

        /// <summary>
        ///   A flag used for tracking whether the selected tab has been clicked recently.
        /// </summary>
        private bool _selectedTabClicked = false;

        /// <summary>
        ///   The Popup containing the selected Tab's RibbonGroups.
        /// </summary>
        private Popup _selectedTabGroupsPopup;
        
        /// <summary>
        ///   The RibbonTabPanel containing the Ribbon's RibbonTabs.
        /// </summary>
        private RibbonTabPanel _ribbonTabPanel;
        
        /// <summary>
        ///   The ScrollViewer containing the RibbonGroups in the active RibbonTab.
        /// </summary>
        private HorizontalScrollViewer _ribbonGroupsContainerScrollViewer;

        #endregion

        #region Constuctors

        /// <summary>
        ///   Initializes static members of the Ribbon class.  This also overrides the
        ///   default style and adds command bindings for some Window control commands.
        /// </summary>
        static Ribbon()
        {
            Type ownerType = typeof(Ribbon);

            if (ownerType.Assembly.GlobalAssemblyCache)
            {
                throw new SecurityException("This assembly is not vetted for safe use by partial trust code, therefore it should not be placed in the Global Assembly Cache.");
            }

            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));

            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(RibbonCommands.CloseWindow, new ExecutedRoutedEventHandler(OnWindowCommandExecute), new CanExecuteRoutedEventHandler(OnWindowCommandCanExecute)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(RibbonCommands.MinimizeWindow, new ExecutedRoutedEventHandler(OnWindowCommandExecute), new CanExecuteRoutedEventHandler(OnWindowCommandCanExecute)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(RibbonCommands.MaximizeWindow, new ExecutedRoutedEventHandler(OnWindowCommandExecute), new CanExecuteRoutedEventHandler(OnWindowCommandCanExecute)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(RibbonCommands.RestoreWindow, new ExecutedRoutedEventHandler(OnWindowCommandExecute), new CanExecuteRoutedEventHandler(OnWindowCommandCanExecute)));

            EventManager.RegisterClassHandler(ownerType, Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk));
            EventManager.RegisterClassHandler(ownerType, Mouse.PreviewMouseUpOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk));
        }

        /// <summary>
        ///   Initializes a new instance of the Ribbon class and hooks the Loaded event
        ///   to perform class initialization.
        /// </summary>
        public Ribbon()
        {
            this.Loaded += new RoutedEventHandler(this.OnLoaded);
        }

        #endregion

        #region Public Events

        /// <summary>
        ///   Callbacks for the ExpandedEvent.
        /// </summary>
        public event RoutedEventHandler Expanded
        {
            add { AddHandler(ExpandedEvent, value, false); }
            remove { RemoveHandler(ExpandedEvent, value); }
        }

        /// <summary>
        ///   Raised when the Ribbon is expanded (IsCollapsed changes to False).
        /// </summary>
        public static readonly RoutedEvent ExpandedEvent =
                    EventManager.RegisterRoutedEvent(
                            "Expanded",
                            RoutingStrategy.Direct,
                            typeof(RoutedEventHandler),
                            typeof(Ribbon));

        /// <summary>
        ///   Callbacks for the CollapsedEvent.
        /// </summary>
        public event RoutedEventHandler Collapsed
        {
            add { AddHandler(CollapsedEvent, value, false); }
            remove { RemoveHandler(CollapsedEvent, value); }
        }

        /// <summary>
        ///   Raised when the Ribbon is collapsed (IsCollapsed changes to True).
        /// </summary>
        public static readonly RoutedEvent CollapsedEvent =
                    EventManager.RegisterRoutedEvent(
                            "Collapsed",
                            RoutingStrategy.Direct,
                            typeof(RoutedEventHandler),
                            typeof(Ribbon));

        /// <summary>
        ///   Raised when the currently selected tab changes.
        /// </summary>
        public event DependencyPropertyChangedEventHandler SelectedTabChanged;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the Visibility for the Icon of the RibbonWindow that contains this Ribbon.
        /// </summary>
        public Visibility WindowIconVisibility
        {
            get { return (Visibility)GetValue(WindowIconVisibilityProperty); }
            set { SetValue(WindowIconVisibilityProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for WindowIconVisibility.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty WindowIconVisibilityProperty = DependencyProperty.Register(
            "WindowIconVisibility", 
            typeof(Visibility), 
            typeof(Ribbon), 
            new UIPropertyMetadata(Visibility.Visible, OnWindowIconVisibilityChanged));

        /// <summary>
        ///   Gets a value indicating whether the Ribbon is currently hosted in a RibbonWindow.
        /// </summary>
        public bool IsHostedInRibbonWindow
        {
            get { return (bool)GetValue(IsHostedInRibbonWindowProperty); }
            private set { SetValue(IsHostedInRibbonWindowPropertyKey, value); }
        }

        /// <summary>
        /// DependencyPropertyKey for read only DependencyProperty IsHostedInRibbonWindow.
        /// </summary>
        public static readonly DependencyPropertyKey IsHostedInRibbonWindowPropertyKey =
                    DependencyProperty.RegisterReadOnly(
                            "IsHostedInRibbonWindow",
                            typeof(bool),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(false));

        /// <summary>
        /// Using a DependencyProperty as the backing store for IsHostedInRibbonWindow.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsHostedInRibbonWindowProperty =
            IsHostedInRibbonWindowPropertyKey.DependencyProperty;

        /// <summary>
        ///   Gets a Collection of the Ribbon's RibbonTabs.
        /// </summary>
        public Collection<RibbonTab> Tabs
        {
            get
            {
                if (_tabs == null)
                {
                    _tabs = new ObservableCollection<RibbonTab>();
                    _tabs.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnTabCollectionChanged);
                }

                return _tabs;
            }
        }

        /// <summary>
        ///   Gets a Collection of the Ribbon's RibbonContextualTabGroups.
        /// </summary>
        public Collection<RibbonContextualTabGroup> ContextualTabGroups
        {
            get
            {
                if (_tabGroups == null)
                {
                    _tabGroups = new ObservableCollection<RibbonContextualTabGroup>();
                    _tabGroups.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnContextualTabGroupsCollectionChanged);
                }

                return _tabGroups;
            }
        }

        /// <summary>
        ///   Gets or sets the currently selected RibbonTab.
        /// </summary>
        public RibbonTab SelectedTab
        {
            get { return (RibbonTab)GetValue(SelectedTabProperty); }
            set { SetValue(SelectedTabProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for SelectedTabProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty SelectedTabProperty =
                    DependencyProperty.Register(
                            "SelectedTab",
                            typeof(RibbonTab),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSelectedTabChanged)));

        /// <summary>
        ///   Gets or sets the Ribbon's application menu.
        /// </summary>
        public RibbonApplicationMenu ApplicationMenu
        {
            get { return (RibbonApplicationMenu)GetValue(ApplicationMenuProperty); }
            set { SetValue(ApplicationMenuProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for ApplicationMenuProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty ApplicationMenuProperty =
                    DependencyProperty.Register(
                            "ApplicationMenu",
                            typeof(RibbonApplicationMenu),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnApplicationMenuChanged)));

        /// <summary>
        ///   Gets or sets the Ribbon's QuickAccessToolbar.
        /// </summary>
        public RibbonQuickAccessToolBar QuickAccessToolBar
        {
            get { return (RibbonQuickAccessToolBar)GetValue(QuickAccessToolBarProperty); }
            set { SetValue(QuickAccessToolBarProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for QuickAccessToolBarProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty QuickAccessToolBarProperty =
                    DependencyProperty.Register(
                            "QuickAccessToolBar",
                            typeof(RibbonQuickAccessToolBar),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnQuickAccessToolBarChanged)));

        /// <summary>
        ///   Gets or sets a value indicating whether the Ribbon is minimized.  When the Ribbon
        ///   is minimized its tabs must be clicked in order for their contents to be displayed
        ///   in a Popup.
        /// </summary>
        public bool IsMinimized
        {
            get { return (bool)GetValue(IsMinimizedProperty); }
            set { SetValue(IsMinimizedProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for IsMinimizedProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsMinimizedProperty =
                    DependencyProperty.Register(
                            "IsMinimized",
                            typeof(bool),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsMinimizedChanged)));

        /// <summary>
        ///   Gets or sets a value indicating whether the RibbonTab's Popup is displayed.
        /// </summary>
        public bool IsOpen
        {
            get { return (bool)GetValue(IsOpenProperty); }
            set { SetValue(IsOpenProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for IsOpenProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsOpenProperty =
                    DependencyProperty.Register(
                            "IsOpen",
                            typeof(bool),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnIsOpenChanged), new CoerceValueCallback(OnCoerceIsOpen)));

        /// <summary>
        ///   Gets a value indicating whether the Ribbon is collapsed.
        /// </summary>
        public bool IsCollapsed
        {
            get { return (bool)GetValue(IsCollapsedProperty); }
            internal set { SetValue(IsCollapsedPropertyKey, value); }
        }

        /// <summary>
        ///   Using a DependencyPropertyKey as the backing store for IsCollapsedProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        private static readonly DependencyPropertyKey IsCollapsedPropertyKey =
                    DependencyProperty.RegisterReadOnly(
                            "IsCollapsed",
                            typeof(bool),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged)));

        /// <summary>
        /// The Dependency Property for IsCollapsed Getter
        /// </summary>
        public static readonly DependencyProperty IsCollapsedProperty = IsCollapsedPropertyKey.DependencyProperty;

        /// <summary>
        ///   Gets or sets the Title of the Ribbon.
        /// </summary>
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for TitleProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty TitleProperty =
                    DependencyProperty.Register(
                            "Title",
                            typeof(string),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(String.Empty, null, new CoerceValueCallback(OnCoerceTitle)));

        /// <summary>
        ///   Gets or sets a value indicating whether to show the QuickAccessToolbar on top of the Ribbon.
        /// </summary>
        public bool ShowQuickAccessToolbarOnTop
        {
            get { return (bool)GetValue(ShowQuickAccessToolbarOnTopProperty); }
            set { SetValue(ShowQuickAccessToolbarOnTopProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for ShowQuickAccessToolbarOnTopProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty ShowQuickAccessToolbarOnTopProperty =
                    DependencyProperty.Register(
                            "ShowQuickAccessToolbarOnTop",
                            typeof(bool),
                            typeof(Ribbon),
                            new FrameworkPropertyMetadata(true));

        #endregion

        #region Internal Properties

        /// <summary>
        ///   Gets a collectino of the Ribbon's contextual RibbonTabs.
        /// </summary>
        internal Collection<RibbonTab> ContextualTabs
        {
            get
            {
                if (_contextualTabs == null)
                {
                    _contextualTabs = new ObservableCollection<RibbonTab>();
                    _contextualTabs.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnContextualTabCollectionChanged);
                }

                return _contextualTabs;
            }
        }

        /// <summary>
        ///   Gets or sets Ribbon's title panel.
        /// </summary>
        internal RibbonTitlePanel RibbonTitlePanel
        {
            get;
            set;
        }

        #endregion

        #region Protected Properties

        /// <summary>
        ///   Gets an enumerator for the Ribbon's logical children.
        /// </summary>
        protected override IEnumerator LogicalChildren
        {
            get { return new RibbonLogicalChildrenEnumerator(this.ApplicationMenu, this.QuickAccessToolBar, this.Tabs, this.ContextualTabGroups); }
        }

        #endregion

        #region Private Properties

        /// <summary>
        ///   Gets the Window containing the Ribbon.
        /// </summary>
        private Window Window
        {
            get { return Window.GetWindow(this); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        ///   Invoked whenever the control's template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (!BrowserInteropHelper.IsBrowserHosted)
            {
                this.IsHostedInRibbonWindow = this.CheckIsHostedInRibbonWindow();
            }

            Window window = Window;

            if (window != null)
            {
                // Remove the handler if already attached
                window.SizeChanged -= new SizeChangedEventHandler(this.OnWindowSizeChanged);
                window.SizeChanged += new SizeChangedEventHandler(this.OnWindowSizeChanged);
            }

            // Disconnect the old panel
            if (_ribbonTabPanel != null)
            {
                _ribbonTabPanel.Children.Clear();
            }

            _ribbonTabPanel = this.GetTemplateChild("PART_TABSHOST") as RibbonTabPanel;

            // Add Tabs to the new panel
            if (_ribbonTabPanel != null)
            {
                foreach (RibbonTab tab in this.Tabs)
                {
                    _ribbonTabPanel.Children.Add(tab);
                }
            }

            if (this.RibbonTitlePanel != null)
            {
                for (int i = 0; i < this.RibbonTitlePanel.Children.Count; i++)
                {
                    if (this.RibbonTitlePanel.Children[i] is RibbonContextualTabGroup)
                    {
                        this.RibbonTitlePanel.Children.RemoveAt(i);
                        i--;
                    }
                }
            }

            ContextualTabs.Clear();

            this.RibbonTitlePanel = this.GetTemplateChild("PART_TITLEHOST") as RibbonTitlePanel;

            if (this.RibbonTitlePanel != null)
            {
                foreach (RibbonContextualTabGroup group in this.ContextualTabGroups)
                {
                    if (group.IsActive)
                    {
                        this.ActivateContextualTabGroup(group);
                    }
                }
            }

            _ribbonGroupsContainerScrollViewer = this.GetTemplateChild("PART_SELECTEDGROUPSHOST") as HorizontalScrollViewer;
            _selectedTabGroupsPopup = this.GetTemplateChild("PART_SELECTEDGROUPSPOPUP") as Popup;

            if (_ribbonGroupsContainerScrollViewer != null)
            {
                _ribbonGroupsContainerScrollViewer.Content = (this.SelectedTab == null) ? null : this.SelectedTab.RibbonGroupsContainer;
            }
        }

        #endregion

        #region Internal Methods

        /// <summary>
        ///   Activates the RibbonContextualTabGroup by adding it to the RibbonTitlePanel.
        /// </summary>
        /// <param name="group">The RibbonContextualTabGroup to activate.</param>
        internal void ActivateContextualTabGroup(RibbonContextualTabGroup group)
        {
            if (this.RibbonTitlePanel != null)
            {
                foreach (RibbonTab tab in group.Tabs)
                {
                    if (!this.ContextualTabs.Contains(tab))
                    {
                        this.ContextualTabs.Add(tab);
                    }
                }

                if (!this.RibbonTitlePanel.Children.Contains(group))
                {
                    this.RibbonTitlePanel.Children.Add(group);
                }
            }
        }

        /// <summary>
        ///   Deactivates the RibbonContextualTabGroup by removing it from the RibbonTitlePanel.
        /// </summary>
        /// <param name="group">The RibbonContextualTabGroup to deactivate.</param>
        internal void DeactivateTabGroup(RibbonContextualTabGroup group)
        {
            foreach (RibbonTab tab in group.Tabs)
            {
                if (this.ContextualTabs.Contains(tab))
                {
                    if (tab.IsSelected)
                    {
                        this.SelectedTab = this.Tabs[0];
                    }

                    this.ContextualTabs.Remove(tab);
                }
            }

            if (this.RibbonTitlePanel != null)
            {
                this.RibbonTitlePanel.Children.Remove(group);
            }

            this.AddLogicalChild(group); // Keep the logical link that panel removed
        }

        /// <summary>
        ///     A callback for handling clicks to a RibbonTab.
        /// </summary>
        /// <param name="ribbonTab">The RibbonTab that was clicked.</param>
        /// <param name="e">The event data.</param>
        internal void NotifyMouseClickedOnTab(RibbonTab ribbonTab, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                // Single clicking should:
                //
                // 1. If maximized, select the clicked tab.
                // 2. If minimized and clicking the previously selected tab.
                //    * Toggle the pop-up for the selected tab.
                // 3. If minimized and clicking an un-selected tab.
                //    * Display the pop-up for the clicked tab.
                if (this.SelectedTab != ribbonTab)
                {
                    this.SelectedTab = ribbonTab;
                    if (this.IsMinimized)
                    {
                        this.IsOpen = true;
                    }

                    _selectedTabClicked = false;
                }
                else
                {
                    if (this.IsMinimized)
                    {
                        this.IsOpen = !this.IsOpen;
                    }

                    _selectedTabClicked = true;
                }
            }
            else if (e.ClickCount == 2)
            {
                // Double-clicking should:
                //
                // 1. If clicking a tab that is being clicked in its 'selected' state for
                //    the second time, toggle its 'IsMinimized' behavior.
                // 2. Otherwise do nothing.
                if (_selectedTabClicked == true || this.IsMinimized)
                {
                    this.IsMinimized = !this.IsMinimized;
                    _selectedTabClicked = false;
                }
                else
                {
                    _selectedTabClicked = true;
                }
            }
            else if (e.ClickCount == 3)
            {
                // Triple-clicking should do the following for initial condiitons (1st click):
                //
                // 1. If minimized:
                //    * Maximize and select the tab.
                // 2. If maximized and the tab was initially selected.
                //    * Minimize and display the pop-up.
                // 3. If maximized and the tab was NOT initially selected.
                //    * Minimize do not display any pop-ups.
                if (_selectedTabClicked == true)
                {
                    this.IsMinimized = !this.IsMinimized;
                }
                else
                {
                    this.IsOpen = true;
                }
            }
        }

        /// <summary>
        ///   Notify the Ribbon that the RibbonContextualTabGroup was clicked.
        /// </summary>
        /// <param name="group">The RibbonContextualTabGroup that was clicked.</param>
        internal void NotifyMouseClickedOnContextualTabGroup(RibbonContextualTabGroup group)
        {
            if (group.Tabs.Count > 0)
            {
                // If Ribbon is minimized - we should open it first
                this.IsMinimized = false;
                this.SelectedTab = group.Tabs[0];
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        ///   Called when the Ribbon loses mouse capture.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnLostMouseCapture(MouseEventArgs e)
        {
            if (Mouse.Captured != this)
            {
                UIElement selectedTabGroupsPopupChild = _selectedTabGroupsPopup.Child;
                if (e.OriginalSource == this)
                {
                    // If Ribbon loses capture because something outside popup is clicked - close the popup
                    if (Mouse.Captured == null || !selectedTabGroupsPopupChild.IsAncestorOf(Mouse.Captured as DependencyObject))
                    {
                        this.IsOpen = false;
                    }
                }
                else
                {
                    // If control inside Ribbon loses capture - restore capture to Ribbon
                    if (selectedTabGroupsPopupChild.IsAncestorOf(e.OriginalSource as DependencyObject))
                    {
                        if (this.IsOpen && Mouse.Captured == null)
                        {
                            Mouse.Capture(this, CaptureMode.SubTree);
                            e.Handled = true;
                        }
                    }
                    else
                    {
                        this.IsOpen = false;
                    }
                }
            }

            base.OnLostMouseCapture(e);
        }

        /// <summary>
        ///   Called when the MouseWheel changes position while the mouse pointer is over the
        ///   Ribbon.  In this case, the MouseWheelEvent is used to indicate that we should
        ///   iterate to the previous or next tab.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            if (!this.IsMinimized && this.SelectedTab != null)
            {
                int selectedTabIndex = _ribbonTabPanel.Children.IndexOf(this.SelectedTab);
                if (e.Delta < 0)
                {
                    if (selectedTabIndex < _ribbonTabPanel.Children.Count - 1)
                    {
                        this.SelectedTab = _ribbonTabPanel.Children[selectedTabIndex + 1] as RibbonTab;
                    }
                }
                else
                {
                    if (selectedTabIndex > 0)
                    {
                        this.SelectedTab = _ribbonTabPanel.Children[selectedTabIndex - 1] as RibbonTab;
                    }
                }

                e.Handled = true;
            }

            base.OnPreviewMouseWheel(e);
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// Property Changed CallBack for IconVisibility. This call back handler 
        /// calls ChangeRibbonWindowIconVisibility which propogates the changes to the Ribbon window.
        /// </summary>
        /// <param name="d">The Sender</param>
        /// <param name="e">DependencyPropertyChangedEventArgs For the changed event</param>
        private static void OnWindowIconVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Ribbon rib = (Ribbon) d;
            if (rib != null)
            {
                if (rib.IsHostedInRibbonWindow)
                {
                    rib.ChangeRibbonWindowIconVisibility();
                }
            }
        }

        /// <summary>
        /// This method propogates the IconVisibility property to the Containing RibbonWindow
        /// This being a seperate method should keep the compiler from killing xbaps.
        /// </summary>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private void ChangeRibbonWindowIconVisibility()
        {
            RibbonWindow rw = (RibbonWindow)this.Window;
            if (rw != null)
            {
                rw.IconVisibility = this.WindowIconVisibility;
            }
        }

        /// <summary>
        /// Event Handler for Preview Mouse Down Outside Captured Element Event and Preview Outside Captured Element Event 
        /// </summary>
        /// <param name="sender">The Ribbon</param>
        /// <param name="e">The Mouse button event args</param>
        private static void OnClickThroughThunk(object sender, MouseButtonEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;
            if (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right)
            {
                if (Mouse.Captured == ribbon)
                {
                    ribbon.IsOpen = false;
                    Mouse.Capture(null);
                }
            }
        }
        
        /// <summary>
        ///   Called when the IsOpen property changes.  This means that the one of the RibbonTab's
        ///   popups was either opened or closed.
        /// </summary>
        /// <param name="sender">The Ribbon whose tab opened or closed its popup.</param>
        /// <param name="e">The event data.</param>
        private static void OnIsOpenChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;

            if (ribbon.IsOpen)
            {
                ribbon.OnRibbonTabPopupOpening();
            }
            else
            {
                ribbon.OnRibbonTabPopupClosing();
            }

            // Each RibbonTab needs to update its IsOpen property
            foreach (RibbonTab tab in ribbon.Tabs)
            {
                tab.CoerceValue(RibbonTab.IsOpenProperty);
            }

            foreach (RibbonContextualTabGroup group in ribbon.ContextualTabGroups)
            {
                foreach (RibbonTab tab in group.Tabs)
                {
                    tab.CoerceValue(RibbonTab.IsOpenProperty);
                }
            }
        }

        /// <summary>
        ///   Coerces the value of the IsOpen property.  Always returns false if the Ribbon
        ///   is not minimized.
        /// </summary>
        /// <param name="sender">The Ribbon whose tab state is being coerced.</param>
        /// <param name="value">The new value of the IsOpen property, prior to any coercion attempt.</param>
        /// <returns>The coerced value of the IsOpen property.</returns>
        private static object OnCoerceIsOpen(DependencyObject sender, object value)
        {
            return ((Ribbon)sender).IsMinimized ? value : true;
        }

        /// <summary>
        ///   Called when the IsMinimized property changes.  
        /// </summary>
        /// <param name="sender">The Ribbon being minimized or expanded.</param>
        /// <param name="e">The event data.</param>
        private static void OnIsMinimizedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;

            if (ribbon.IsMinimized)
            {
                ribbon.IsOpen = false;
            }
            else
            {
                ribbon.CoerceValue(IsOpenProperty);

                if (Mouse.Captured == ribbon)
                {
                    Mouse.Capture(null);
                }
            }
        }

        /// <summary>
        ///   Called if the Ribbon's QuickAccessToolbar changes.
        /// </summary>
        /// <param name="sender">The Ribbon whose QuickAccessToolbar is changing.</param>
        /// <param name="e">The event data.</param>
        private static void OnQuickAccessToolBarChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;

            RibbonQuickAccessToolBar oldRibbonQuickAccessToolBar = e.OldValue as RibbonQuickAccessToolBar;
            RibbonQuickAccessToolBar newRibbonQuickAccessToolBar = e.NewValue as RibbonQuickAccessToolBar;

            // Remove Logical tree link
            if (oldRibbonQuickAccessToolBar != null)
            {
                ribbon.RemoveLogicalChild(oldRibbonQuickAccessToolBar);
            }

            // Add Logical tree link
            if (newRibbonQuickAccessToolBar != null)
            {
                ribbon.AddLogicalChild(newRibbonQuickAccessToolBar);
            }
        }

        /// <summary>
        ///   Called when the Ribbon's currently selected tab changes.  This notifies the involved
        ///   tabs that they are either being selected or deselected, and swaps out the ScrollViewer's
        ///   content.
        /// </summary>
        /// <param name="sender">The Ribbon whose currently selected tab is changing.</param>
        /// <param name="e">The event data.</param>
        private static void OnSelectedTabChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;

            RibbonTab oldRibbonTab = e.OldValue as RibbonTab;
            RibbonTab newRibbonTab = e.NewValue as RibbonTab;

            if (oldRibbonTab != null)
            {
                oldRibbonTab.IsSelected = false;
            }

            if (newRibbonTab != null)
            {
                newRibbonTab.IsSelected = true;
                newRibbonTab.BringIntoView();
            }

            // Update the TabGroups
            if (ribbon._ribbonGroupsContainerScrollViewer != null)
            {
                ribbon._ribbonGroupsContainerScrollViewer.Content = (newRibbonTab == null) ? null : newRibbonTab.RibbonGroupsContainer;
            }

            if (ribbon.SelectedTabChanged != null)
            {
                ribbon.SelectedTabChanged(sender, e);
            }
        }

        /// <summary>
        ///   Called when the Ribbon's ApplicationMenu changes.
        /// </summary>
        /// <param name="sender">The Ribbon whose ApplicationMenu has changed.</param>
        /// <param name="e">The event data.</param>
        private static void OnApplicationMenuChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;

            RibbonApplicationMenu oldRibbonApplicationMenu = e.OldValue as RibbonApplicationMenu;
            RibbonApplicationMenu newRibbonApplicationMenu = e.NewValue as RibbonApplicationMenu;

            // Remove Logical tree link
            if (oldRibbonApplicationMenu != null)
            {
                ribbon.RemoveLogicalChild(oldRibbonApplicationMenu);
            }

            // Add Logical tree link
            if (newRibbonApplicationMenu != null)
            {
                ribbon.AddLogicalChild(newRibbonApplicationMenu);
            }
        }

        /// <summary>
        ///   Coerces the Title property.  Return Window.Title if value is not set.
        /// </summary>
        /// <param name="sender">
        ///   The Ribbon that the Title property exists on.  When the callback is invoked,
        ///   the property system will pass this value.
        /// </param>
        /// <param name="value">The new value of the Title property, prior to any coercion attempt.</param>
        /// <returns>The coerced value of the Title property.</returns>
        private static object OnCoerceTitle(DependencyObject sender, object value)
        {
            Ribbon ribbon = (Ribbon)sender;
            Window window = ribbon.Window;

            if (window != null && string.IsNullOrEmpty(value as string))
            {
                return window.Title;
            }

            return value;
        }

        /// <summary>
        ///   Called when the IsCollapsed property changes.
        /// </summary>
        /// <param name="sender">The Ribbon being collapsed or expanded.</param>
        /// <param name="e">The event data.</param>
        private static void OnIsCollapsedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)sender;
            ribbon.RaiseEvent(new RoutedEventArgs(ribbon.IsCollapsed ? CollapsedEvent : ExpandedEvent));
        }

        /// <summary>
        ///   Called when one of the class-bound RibbonCommands is executed.
        /// </summary>
        /// <param name="target">The target of the command's execution.</param>
        /// <param name="e">The event data.</param>
        private static void OnWindowCommandExecute(object target, ExecutedRoutedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)target;

            Window window = ribbon.Window;

            if (window != null && (ribbon.IsHostedInRibbonWindow || e.Command == RibbonCommands.CloseWindow))
            {
                if (e.Command == RibbonCommands.MaximizeWindow)
                {
                    window.WindowState = WindowState.Maximized;
                }
                else if (e.Command == RibbonCommands.MinimizeWindow)
                {
                    window.WindowState = WindowState.Minimized;
                }
                else if (e.Command == RibbonCommands.RestoreWindow)
                {
                    window.WindowState = WindowState.Normal;
                }
                else if (e.Command == RibbonCommands.CloseWindow)
                {
                    window.Close();
                }
            }
        }

        /// <summary>
        ///   Called to query whether the given command can execute.
        /// </summary>
        /// <param name="target">The proposed target of the command's execution.</param>
        /// <param name="e">The event data.</param>
        private static void OnWindowCommandCanExecute(object target, CanExecuteRoutedEventArgs e)
        {
            Ribbon ribbon = (Ribbon)target;

            Window window = ribbon.Window;

            if (window != null)
            {
                if (e.Command == RibbonCommands.CloseWindow ||
                    e.Command == RibbonCommands.MinimizeWindow ||
                    e.Command == RibbonCommands.MaximizeWindow ||
                    e.Command == RibbonCommands.RestoreWindow)
                {
                    e.CanExecute = true;
                }
            }
        }

        /// <summary>
        ///   Called when the Ribbon's popped up tab is closing.
        /// </summary>
        private void OnRibbonTabPopupClosing()
        {
            if (Mouse.Captured == this)
            {
                Mouse.Capture(null);
            }
        }

        /// <summary>
        ///   Called when the Ribbon's selected tab is opening in popup mode.
        /// </summary>
        private void OnRibbonTabPopupOpening()
        {
            if (this.IsMinimized)
            {
                if (BrowserInteropHelper.IsBrowserHosted)
                {
                    _selectedTabGroupsPopup.Width = ((FrameworkElement)_selectedTabGroupsPopup.Parent).RenderSize.Width;
                }
                else
                {
                    _selectedTabGroupsPopup.Width = this.CalculatePopupWidth();
                }

                Mouse.Capture(this, CaptureMode.SubTree);
            }
        }

        /// <summary>
        ///   Called when the ContextualTabGroups collection changes.  In this method we maintain
        ///   the logical tree.
        /// </summary>
        /// <param name="sender">The Ribbon whose ContextualTabGroups collection changed.</param>
        /// <param name="e">The event data.</param>
        private void OnContextualTabGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Reset:
                    foreach (RibbonContextualTabGroup group in this.ContextualTabGroups)
                    {
                        this.AddLogicalChild(group);

                        if (group.IsActive)
                        {
                            this.ActivateContextualTabGroup(group);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Add:
                    foreach (RibbonContextualTabGroup group in e.NewItems)
                    {
                        this.AddLogicalChild(group);

                        if (group.IsActive)
                        {
                            this.ActivateContextualTabGroup(group);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (RibbonContextualTabGroup group in e.OldItems)
                    {
                        this.RemoveLogicalChild(group);

                        if (group.IsActive)
                        {
                            this.DeactivateTabGroup(group);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.NewItems.Count == 1 && e.OldItems.Count == 1)
                    {
                        RibbonContextualTabGroup oldRibbonContextualTabGroup = e.OldItems[0] as RibbonContextualTabGroup;
                        RibbonContextualTabGroup newRibbonContextualTabGroup = e.NewItems[0] as RibbonContextualTabGroup;

                        this.RemoveLogicalChild(oldRibbonContextualTabGroup);
                        this.AddLogicalChild(newRibbonContextualTabGroup);

                        if (newRibbonContextualTabGroup.IsActive)
                        {
                            this.ActivateContextualTabGroup(newRibbonContextualTabGroup);
                        }

                        if (oldRibbonContextualTabGroup.IsActive)
                        {
                            this.DeactivateTabGroup(oldRibbonContextualTabGroup);
                        }
                    }

                    break;
            }
        }

        /// <summary>
        ///   Called whenever the Tabs collection is modified.  This maintains the logical
        ///   tree and updates the RibbonTabPanel.
        /// </summary>
        /// <param name="sender">The Tabs collection which raised the event.</param>
        /// <param name="e">Information about the event.</param>
        private void OnTabCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Reset:
                    if (_ribbonTabPanel != null)
                    {
                        _ribbonTabPanel.Children.Clear();
                    }

                    foreach (RibbonTab tab in this.Tabs)
                    {
                        this.AddLogicalChild(tab);

                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Add(tab);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Add:
                    foreach (RibbonTab tab in e.NewItems)
                    {
                        this.AddLogicalChild(tab);

                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Add(tab);
                        }

                        if (this.SelectedTab == null)
                        {
                            this.SelectedTab = tab;
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (RibbonTab tab in e.OldItems)
                    {
                        this.RemoveLogicalChild(tab);

                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Remove(tab);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.NewItems.Count == 1 && e.OldItems.Count == 1)
                    {
                        this.RemoveLogicalChild(e.OldItems[0]);
                        this.AddLogicalChild(e.NewItems[0]);

                        if (_ribbonTabPanel != null)
                        {
                            int index = _ribbonTabPanel.Children.IndexOf(e.OldItems[0] as RibbonTab);

                            if (index >= 0)
                            {
                                RibbonTab tab = e.NewItems[0] as RibbonTab;
                                _ribbonTabPanel.Children[index] = tab;
                            }
                        }
                    }

                    break;
            }
        }

        /// <summary>
        ///   Called when the Ribbon's ContextualTab collection changes.
        /// </summary>
        /// <param name="sender">The Ribbon whose ContextualTabs collection has changed.</param>
        /// <param name="e">The event data.</param>
        private void OnContextualTabCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Note: No logical connection is necessary because contextual RibbonTabs are logically connected to ContextualTabGroup
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Reset:
                    if (_ribbonTabPanel != null)
                    {
                        _ribbonTabPanel.Children.Clear();
                    }

                    foreach (RibbonTab tab in this.Tabs)
                    {
                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Add(tab);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Add:
                    foreach (RibbonTab tab in e.NewItems)
                    {
                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Add(tab);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (RibbonTab tab in e.OldItems)
                    {
                        if (_ribbonTabPanel != null)
                        {
                            _ribbonTabPanel.Children.Remove(tab);
                        }
                    }

                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.NewItems.Count == 1 && e.OldItems.Count == 1)
                    {
                        if (_ribbonTabPanel != null)
                        {
                            int index = _ribbonTabPanel.Children.IndexOf(e.OldItems[0] as RibbonTab);
                            if (index >= 0)
                            {
                                RibbonTab tab = e.NewItems[0] as RibbonTab;
                                _ribbonTabPanel.Children[index] = tab;
                            }
                        }
                    }

                    break;
            }
        }

        /// <summary>
        ///   Called when the Ribbon is loaded.  This creates its QuickAccessToolbar and
        ///   ApplicationMenu, and also selects the initial tab.
        /// </summary>
        /// <param name="sender">The Ribbon being loaded.</param>
        /// <param name="e">The event data.</param>
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.Loaded -= new RoutedEventHandler(this.OnLoaded);

            // Create default RibbonApplicationMenu if ApplicationMenu is not set
            if (this.ApplicationMenu == null)
            {
                this.ApplicationMenu = new RibbonApplicationMenu();
            }

            // Create default RibbonQuickAccessToolBar if QuickAccessToolBar is not set
            if (this.QuickAccessToolBar == null)
            {
                this.QuickAccessToolBar = new RibbonQuickAccessToolBar();
            }

            // Select the first Tab if nothing is selected
            if (this.SelectedTab == null && this.Tabs.Count > 0)
            {
                this.SelectedTab = this.Tabs[0];
            }
        }

        /// <summary>
        ///   Returns a value indicating whether the Ribbon is hosted in a RibbonWindow.
        /// </summary>
        /// <returns>Returns true if the Ribbon is hosted in a RibbonWindow.</returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private bool CheckIsHostedInRibbonWindow()
        {
            return Window is RibbonWindow;
        }

        /// <summary>
        ///   Calculate the Width of the Ribbon's popup.
        /// </summary>
        /// <returns>The width of the popup.</returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private double CalculatePopupWidth()
        {
            // 1. Calculate _popupPlacementTarget bounding rect in screen coordinates
            // 2. Get monitor for _popupPlacementTarget rect
            // 3. Get monitor size
            // 4. intersect monitor rect with _popupPlacementTarget rect
            // 5. return the width of the intersection
            FrameworkElement popupPlacementTarget = _selectedTabGroupsPopup.Parent as FrameworkElement;

            Point startPoint = popupPlacementTarget.PointToScreen(new Point());
            Point endPoint = popupPlacementTarget.PointToScreen(new Point(popupPlacementTarget.ActualWidth, popupPlacementTarget.ActualHeight));

            NativeMethods.RECT popupPlacementTargetRect = new NativeMethods.RECT();
            popupPlacementTargetRect.left = (int)startPoint.X;
            popupPlacementTargetRect.right = (int)endPoint.X;
            popupPlacementTargetRect.top = (int)startPoint.Y;
            popupPlacementTargetRect.bottom = (int)endPoint.Y;
            IntPtr monitorPtr = NativeMethods.MonitorFromRect(ref popupPlacementTargetRect, NativeMethods.MONITOR_DEFAULTTONEAREST);
            if (monitorPtr != IntPtr.Zero)
            {
                NativeMethods.MONITORINFOEX monitorInfo = new NativeMethods.MONITORINFOEX();

                NativeMethods.GetMonitorInfo(new HandleRef(null, monitorPtr), monitorInfo);
                NativeMethods.RECT rect = monitorInfo.rcMonitor;

                Rect screenRect = new Rect(new Point(rect.left, rect.top), new Point(rect.right, rect.bottom));
                Rect popupRect = new Rect(startPoint, endPoint);
                screenRect.Intersect(popupRect);

                double screenWidth = Math.Abs(popupPlacementTarget.PointFromScreen(screenRect.BottomRight).X -
                                        popupPlacementTarget.PointFromScreen(screenRect.TopLeft).X);
                return screenWidth + (screenRect.Right == popupRect.Right ? 5 : 0); // Account for 5px popup shadow
            }

            return popupPlacementTarget.RenderSize.Width;
        }

        /// <summary>
        ///   Called when the Window hosting the Ribbon changes sizes.  Here we decide
        ///   whether or nto to collapse the Ribbon.
        /// </summary>
        /// <param name="sender">The Window whose size has changed.</param>
        /// <param name="e">The event data.</param>
        private void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
        {
            Window window = (Window)sender;
            this.IsCollapsed = !(window.ActualWidth > Ribbon.CollapseWidth && window.ActualHeight > Ribbon.CollapseHeight);
        }

        #endregion

        #region RibbonLogicalChildEnumerator Class

        /// <summary>
        ///   An enumerator for the logical children of the Ribbon.
        /// </summary>
        private class RibbonLogicalChildrenEnumerator : IEnumerator
        {
            #region Fields

            /// <summary>
            ///   The Ribbon's ApplicationMenu. 
            /// </summary>
            private RibbonApplicationMenu _ribbonApplicationMenu;

            /// <summary>
            ///   The Ribbon's QuickAccessToolbar.
            /// </summary>
            private RibbonQuickAccessToolBar _ribbonQuickAccessToolBar;

            /// <summary>
            ///   The Ribbon's collection of tabs.
            /// </summary>
            private Collection<RibbonTab> _tabs;

            /// <summary>
            ///   The Ribbon's collectino of ContextualTabGroups.
            /// </summary>
            private Collection<RibbonContextualTabGroup> _contextualTabGroups;

            /// <summary>
            ///   The current position of enumeration.
            /// </summary>
            private Position _postition;

            /// <summary>
            ///   The current tab index if we are currently enumerating the Ribbon's tabs.
            /// </summary>
            private int _tabIndex = -1;

            #endregion

            #region Constructors

            /// <summary>
            ///   Initializes a new instance of the RibbonLogicalChildrenEnumerator class.
            /// </summary>
            /// <param name="ribbonApplicationMenu">The RibbonApplicationMenu of the Ribbon to be enumerated.</param>
            /// <param name="ribbonQuickAccessToolBar">The RibbonQuickAccessToolbar of the Ribbon to be enumerated.</param>
            /// <param name="tabs">The collection of RibbonTabs of the Ribbon to be enumerated.</param>
            /// <param name="contextualTabGroups">The collection of RibbonContextualTabGroups of the Ribbon to be enumerated.</param>
            public RibbonLogicalChildrenEnumerator(
                RibbonApplicationMenu ribbonApplicationMenu,
                RibbonQuickAccessToolBar ribbonQuickAccessToolBar,
                Collection<RibbonTab> tabs,
                Collection<RibbonContextualTabGroup> contextualTabGroups)
            {
                _postition = Position.None;
                _ribbonApplicationMenu = ribbonApplicationMenu;
                _ribbonQuickAccessToolBar = ribbonQuickAccessToolBar;
                _tabs = tabs;
                _contextualTabGroups = contextualTabGroups;
            }

            #endregion

            #region Position Enum

            /// <summary>
            ///   An enum indicating the current position of enumeration.
            /// </summary>
            private enum Position
            {
                /// <summary>
                ///   Indicates that the enumeration is not currently within the Ribbon's
                ///   logical children.
                /// </summary>
                None,

                /// <summary>
                ///   Indicates enumeration is currently at the ApplicationMenu.
                /// </summary>
                ApplicationMenu,

                /// <summary>
                ///   Indicates enumeration is currently at the QuickAccessToolbar.
                /// </summary>
                QuickAccessToolbar,

                /// <summary>
                ///   Indicates enumeration is currently within the Ribbon's tabs.
                /// </summary>
                Tabs,

                /// <summary>
                ///   Indicates enumeration is currently within the Ribbon's selected tab's groups.
                /// </summary>
                Groups,
            }

            #endregion

            #region Public Properties

            /// <summary>
            ///   Gets the object at the enumerators current position.
            /// </summary>
            public object Current
            {
                get
                {
                    switch (_postition)
                    {
                        case Position.ApplicationMenu:
                            return _ribbonApplicationMenu;
                        case Position.QuickAccessToolbar:
                            return _ribbonQuickAccessToolBar;
                        case Position.Tabs:
                            return _tabs[_tabIndex];
                        case Position.Groups:
                            return _contextualTabGroups[_tabIndex];
                    }

                    throw new InvalidOperationException();
                }
            }

            #endregion

            #region Public Methods

            /// <summary>
            ///   Advances the enumerator to the next logical child of the Ribbon.
            /// </summary>
            /// <returns>True if the enumerator was successfully advanced, false otherwise.</returns>
            public bool MoveNext()
            {
                if (_postition == Position.None)
                {
                    _postition = Position.ApplicationMenu;
                    if (_ribbonApplicationMenu != null)
                    {
                        return true;
                    }
                }

                if (_postition == Position.ApplicationMenu)
                {
                    _postition = Position.QuickAccessToolbar;
                    if (_ribbonQuickAccessToolBar != null)
                    {
                        return true;
                    }
                }

                if (_postition == Position.QuickAccessToolbar)
                {
                    _postition = Position.Tabs;
                }

                if (_postition == Position.Tabs)
                {
                    if (_tabIndex < _tabs.Count - 1)
                    {
                        _tabIndex++;
                        return true;
                    }
                    else
                    {
                        _postition = Position.Groups;
                        _tabIndex = -1;
                    }
                }

                if (_postition == Position.Groups)
                {
                    if (_tabIndex < _contextualTabGroups.Count - 1)
                    {
                        _tabIndex++;
                        return true;
                    }
                }

                this.Reset();

                return false;
            }

            /// <summary>
            ///   Resets the RibbonLogicalChildrenEnumerator.
            /// </summary>
            public void Reset()
            {
                _postition = Position.None;
                _tabIndex = -1;
            }

            #endregion
        }

        #endregion
    }
}